<?php

declare(strict_types=1);

namespace App\CustHub\Service;

use App\Common\Constants\StatusConstants;
use App\Common\Robot\Robot;
use App\Common\Service\Affiliate\TransferChainService;
use App\Common\Service\LinkFilterService;
use App\CustHub\Constants\MsgTypeConstants;
use App\CustHub\Mapper\ChRobotMapper;
use App\CustHub\Mapper\ChRoleSchemeMapper;
use App\CustHub\Mapper\ChRoomBroadcastMapper;
use App\CustHub\Mapper\ChRoomMapper;
use App\CustHub\Model\ChRobot;
use App\CustHub\Model\ChRoleScheme;
use App\CustHub\Model\ChRoom;
use App\CustHub\Model\ChRoomBroadcastConfig;
use App\CustHub\Model\ChRoomBroadcastRecord;
use App\CustHub\Model\ChRoomBroadcastRecordTarget;
use App\CustHub\Model\ChRoomBroadcastSource;
use App\Robot\Dto\CommonJobDto;
use App\Robot\Dto\RobotMsgDto;
use App\Robot\Job\CommonJob;
use App\Robot\Service\RobotApiService;
use App\Robot\Service\SendRobotMessageService;
use Carbon\Carbon;
use Hyperf\DbConnection\Db;
use Mine\Abstracts\AbstractService;
use Mine\Annotation\Transaction;
use Mine\Exception\NormalStatusException;
use Mine\MineModel;
use function Hyperf\Collection\collect;

class ChRoomBroadcastService extends AbstractService
{
    public $mapper;

    private TransferChainService $transferChainService;

    private ChRobotMapper $robotMapper;


    private ChRoleSchemeMapper $roleSchemeMapper;

    private RoleSchemeService $roleSchemeService;

    public function __construct(
        ChRoomBroadcastMapper $mapper,
        TransferChainService $transferChainService,
        ChRobotMapper $robotMapper,
        ChRoleSchemeMapper $roleSchemeMapper,
        RoleSchemeService $roleSchemeService
    )
    {
        $this->mapper = $mapper;
        $this->transferChainService = $transferChainService;
        $this->robotMapper = $robotMapper;
        $this->roleSchemeMapper = $roleSchemeMapper;
        $this->roleSchemeService = $roleSchemeService;
    }

    public function list(array $params): array
    {
        $pageSize = $params['page_size'] ?? 10;
        $p = $this->mapper->model::query()
            ->select(['id','name','status','target_room_ids'])
            ->with(['sources'])
            ->where('main_uid',$params['uid'])
            ->orderBy('created_at','desc')
            ->paginate((int)$pageSize);

        $p->getCollection()->transform(function ($item){
           $item->sources_count = count($item->sources);
           $item->target_room_count = count($item->target_room_ids);
           unset($item->sources,$item->target_room_ids);
           return $item;
        });

        return $this->mapper->setPaginate($p);
    }

    #[Transaction]
    public function save(array $data): mixed
    {
        $data = $this->changeData($data);
        $data['name'] = "方案 ".Carbon::now()->toDateTimeString();
        $sources = $data['sources'];
        $id = $this->mapper->save($data);
        foreach ($sources as $k=>$source){
            $sources[$k]['speaker_ids'] = json_encode($source['speaker_ids']);
            $sources[$k]['room_broadcast_id'] = $id;
        }
        ChRoomBroadcastConfig::insert($sources);
        return $id;
    }

    public function getInfo(mixed $id)
    {
        $info = $this->mapper->model::query()
            ->with(['sources'])
            ->where('id',$id)
            ->first();
        if(empty($info)){
            throw new NormalStatusException('请选择方案');
        }
        $info = $info->toArray();

        $sources = $info['sources'];

        foreach ($sources as $k=>$source){
            if($source['source_id'] > 0){
                $sourceInfo = ChRoomBroadcastSource::query()
                    ->where('id',$source['source_id'])
                    ->first();
            }else{
                $sourceInfo = ChRoom::query()
                    ->where('id',$source['room_id'])
                    ->first();
            }
            $sources[$k]['info'] = $sourceInfo;
        }
        $info['sources'] = $sources;

        $roomList = ChRoom::query()
            ->select(['id','room_name','room_avatar_url','room_member_count'])
            ->whereIn('id',$info['target_room_ids'])
            ->get();
        $info['target_room_list'] = $roomList;

        $broadcastList = ChRobot::query()
            ->select(['id','nickname','avatar'])
            ->whereIn('id',$info['broadcast_ids'])
            ->get();

        $info['broadcast_list'] = $broadcastList;

        return $info;
    }

    #[Transaction]
    public function update(mixed $id, array $data): bool
    {
        $data = $this->changeData($data);

        $sources = $data['sources'];
        $rst = $this->mapper->update($id, $data);

        ChRoomBroadcastConfig::query()
            ->where('room_broadcast_id',$id)
            ->delete();
        foreach ($sources as $k=>$source){
            $sources[$k]['speaker_ids'] = json_encode($source['speaker_ids']);
            $sources[$k]['room_broadcast_id'] = $id;
        }
        ChRoomBroadcastConfig::insert($sources);
        return $rst;
    }

    #[Transaction]
    public function copy(array $params): bool
    {
        $info = $this->mapper->model::query()
            ->with('sources')
            ->where('id',$params['id'])
            ->first();

        if(empty($info)){
            throw new NormalStatusException('请选择方案');
        }

        unset($info['id'],$info['created_at'],$info['updated_at']);
        $info->name = $info->name."-复制";

        $sources =  $info->sources->toArray();

        $id = $this->mapper->save($info->toArray());

        foreach ($sources as $k=>$source){
            $sources[$k]['speaker_ids'] = json_encode($source['speaker_ids']);
            $sources[$k]['room_broadcast_id'] = $id;
        }
        ChRoomBroadcastConfig::insert($sources);
        return true;
    }

    public function rename(array $params): bool
    {
        $info = $this->mapper->findById($params['id']);
        if(empty($info)){
            throw new NormalStatusException('请选择方案');
        }
        $info->name = $params['name'];
        return $info->save();
    }


    public function changeStatus(mixed $id, string $value, string $filed = 'status'): bool
    {
        $id = json_decode($id,true);
        return $value == MineModel::ENABLE ? $this->mapper->enable($id, $filed) : $this->mapper->disable($id, $filed);
    }

    #[Transaction]
    public function delete(array $ids): bool
    {
        $this->mapper->delete($ids);
        ChRoomBroadcastConfig::query()
            ->whereIn('room_broadcast_id',$ids)
            ->delete();
        return true;
    }


    private function changeData(array $data): array
    {
        $jsonFields = [
            'sources','time_frame','relay_types','filter_content','replace_list','chain_transfer_config','chain_whitelist','target_room_ids','broadcast_ids','chain_filter_keyword'
        ];

        foreach ($jsonFields as $field){
            if (isset($data[$field])){
                $data[$field] = json_decode($data[$field],true);
            }else{
                $data[$field] = [];
            }
        }

        return  $data;
    }


    /**
     * 获取满足条件的方案
     * @param int $robotId
     * @param string $fromRoomId
     * @return array|\Hyperf\Collection\Collection
     */
    public function getQualifiedPlans(int $robotId,string $fromRoomId): array|\Hyperf\Collection\Collection
    {
        $roomInfo = ChRoom::query()
            ->where('room_wxid',$fromRoomId)
            ->whereHas('robots',function ($query) use ($robotId){
                $query->where('robot_id',$robotId);
            })
            ->first();
        if(empty($roomInfo)){
            return [];
        }

        $firstQuery = Db::table('ch_room_broadcast_config as c')
            ->join('ch_room_broadcast as b', 'c.room_broadcast_id', '=', 'b.id')
            ->where('c.source_id', 0)
            ->where('c.room_id', $roomInfo->id)
            ->where('c.listen_robot_id',$robotId)
            ->where('b.status', StatusConstants::ENABLE)
            ->selectRaw('b.*,c.speaker_ids,c.room_id');

        $secondQuery = Db::table('ch_room_broadcast_config as c')
            ->join('ch_room_broadcast as b', 'c.room_broadcast_id', '=', 'b.id')
            ->join('ch_room_broadcast_source as s', 'c.source_id', '=', 's.id')
            ->where('c.source_id', '<>',0)
            ->where('s.room_id', $roomInfo->id)
            ->where('s.listen_robot_id',$robotId)
            ->where('b.status', StatusConstants::ENABLE)
            ->selectRaw('b.*,s.speaker_ids,c.room_id');

        $result = $secondQuery->union($firstQuery)->get();

        return $result;
    }


    public function handle(ChRobot $robot,array $data)
    {
        $fromRoomId = $data['from_room_id'];
        $robotId = $robot->id;
        $sender = $data['sender'];

        $msgContent = $data['content'];
        $content = $msgContent['content'];
        $msgType = $msgContent['msg_type'];


        $plans = $this->getQualifiedPlans($robotId,$fromRoomId);
        if(empty($plans)){
            return;
        }
        foreach ($plans as $plan){
            $plan = collect($plan)->toArray();
            //转发时间段
            $time_frame = json_decode($plan['time_frame'],true);
            $startTime = Carbon::now()->toDateString().' '.$time_frame['start'];
            $endTime = Carbon::now()->toDateString().' '.$time_frame['end'];
            //过滤内容
            $filterContent = json_decode($plan['filter_content'],true);
            $isSkip = false;
            foreach ($filterContent as $filter){
                if(str_contains($content,$filter)){
                    $isSkip = true;
                    break;
                }
            }
            if($isSkip){
                continue;
            }

            if(Carbon::parse($endTime)->lt(Carbon::parse($startTime))){
                $endTime = Carbon::parse($endTime)->addDay()->toDateTimeString();
            }

            if(Carbon::now()->lt($startTime) || Carbon::now()->gt($endTime)){
                continue;
            }

            //主讲人
            $speaker_ids = json_decode($plan['speaker_ids'],true);
            if(!in_array($sender,$speaker_ids)){
                continue;
            }

            //转发内容
            $relay_types = json_decode($plan['relay_types'],true);
            if(!in_array($msgType,$relay_types)){
                continue;
            }
            //替换内容
            $replace_list = json_decode($plan['replace_list'],true);
            foreach ($replace_list as $replace){
                $content = str_replace($replace['match_content'],$replace['replace_content'],$content);
            }

            //转播类型 0=客户群 1=角色方案
            $broadcast_type = $plan['broadcast_type'];
            //转播人
            $broadcast_ids = json_decode($plan['broadcast_ids'],true);
            if($broadcast_type == 0){
                //转播群
                $target_room_ids = json_decode($plan['target_room_ids'],true);
                $broadcast_ids = ChRobot::query()
                    ->whereIn('id',$broadcast_ids)
                    ->where('login_status',StatusConstants::ENABLE)
                    ->pluck('id');
                if($broadcast_ids->isEmpty()){
                    continue;
                }
                $targetRoomList = [];
                foreach ($target_room_ids as $roomId){
                    $targetRoomList[$roomId] = $broadcast_ids->toArray();
                }

            }else{
                $targetRoomList = $this->roleSchemeService->getRoomAndRobotIds($plan['role_plan_id'],$broadcast_ids);
            }


            if(empty($targetRoomList)){
                continue;
            }

            if($msgType == MsgTypeConstants::TEXT){
                $is_auto_chain_transfer = $plan['is_auto_chain_transfer'];
                if($is_auto_chain_transfer){
                    $isContinue = true;
                    if(!empty($plan['chain_filter_keyword'])){
                        $chain_filter_keyword = json_decode($plan['chain_filter_keyword'],true);
                        if(checkKeywords($chain_filter_keyword,$content)){
                            //满足任意一个关键词则不进行转链处理
                            $isContinue = false;
                        }
                    }
                    //是否继续转链
                    if($isContinue){
                        //开启自动转链
                        //自动转链配置
                        $chain_transfer_config = json_decode($plan['chain_transfer_config'],true);
                        $chain_transfer_config = collect($chain_transfer_config)->keyBy('type')->toArray();

                        $ext = [];
                        if($plan['chain_transfer_tpl_switch']){
                            $chain_transfer_tpl = $plan['chain_transfer_tpl'];
                            $chain_transfer_tips = $plan['chain_transfer_tips'];
                            $ext = [
                                'chain_transfer_tpl' => $chain_transfer_tpl,
                                'chain_transfer_tips' => $chain_transfer_tips
                            ];
                        }

                        $rst = $this->transferChainService->tranSendText($content,$chain_transfer_config,$ext);
                        if($rst === false){
                            //转链失败
                            $chain_transfer_fail_skip = $plan['chain_transfer_fail_skip'];
                            if($chain_transfer_fail_skip){
                                //转链失败 则跳过
                                return;
                            }
                            //链接白名单原文如包含链接，且链接不在白名单中，则链接将被替换为空，继续发送原文
                            $chain_whitelist = json_decode($plan['chain_whitelist'],true);
                            if(!empty($chain_whitelist)){
                                $content = (new LinkFilterService($chain_whitelist))->filter($content);
                            }
                        }else{
                            $content = $rst;
                        }
                    }
                }
            }

            if($plan['limit_switch'] || empty($plan['non_text_sleep_time'])){
                $sleepTime = 1;
            }else{
                $sleepTime = (int)ceil($plan['non_text_sleep_time'] / 1000);
            }

            $sleepTime < 0 && $sleepTime = 1;
            $delayTime = 0;

            if($msgType == MsgTypeConstants::IMAGE && $plan['is_image_to_sticker'] == 1){
                //图片转换为表情
                $msgType = MsgTypeConstants::EMOJI;
            }

            $broadcastRecordData = [
                'plan_id' => $plan['id'],
                'listen_robot_id' => $robotId,
                'room_id' => $plan['room_id'],
                'content' => [
                    'msg_type' => $msgType,
                    'content' => $content
                ],
                'msg_data' => $data['msg_data'] ?? []
            ];

            $recordModel = ChRoomBroadcastRecord::create($broadcastRecordData);
            $recordId = $recordModel->id;
            // 获取总 chunk 数
            $totalChunks = ceil(count($targetRoomList) / 100);
            collect($targetRoomList)->chunk(100)->each(function ($item, $chunkIndex) use ($recordId,$data,$broadcast_ids,&$delayTime,$sleepTime,$msgType,$content,$totalChunks){

                $roomIds = collect($item)->keys()->toArray();
                $roomWxidMap = ChRoom::query()
                    ->whereIn('id',$roomIds)
                    ->pluck('room_wxid','id')
                    ->toArray();
                foreach ($item as $k=>$value){
                    if(isset($roomWxidMap[$k])){
                        $roomWxid = $roomWxidMap[$k];
                        $robotId = collect($value)->random(1)->first();
                        $delayTime = $delayTime + $sleepTime;

                        switch ($msgType){
                            case MsgTypeConstants::MINI_PROGRAM:
                            case MsgTypeConstants::VIDEO_CHANNEL:
                            case MsgTypeConstants::BUSINESS_CARD:
                                $sendContent = $data['msg_data'];
                                break;
                            case MsgTypeConstants::URL:
                                $sendContent = $data['msg_data'];
                                $sendContent['title'] = decodeIfBase64($sendContent['title']);
                                $sendContent['desc'] = decodeIfBase64($sendContent['desc']);
                                $sendContent['url'] = $sendContent['link_url'];
                                $sendContent['cover'] = $sendContent['icon_url'];
                                break;
                            case MsgTypeConstants::VIDEO:
                                $msgType = 'qw_video';
                                $sendContent = $data['msg_data'];
                                break;
                            default:
                                $sendContent = $content;
                                break;
                        }

                        $messageData = [
                            'robot_id' => $robotId,
                            'to_id' => $roomWxid,
                            'msg_type' => $msgType,
                            'content' => $sendContent
                        ];
                        $recordTargetData = [
                            'record_id' => $recordId,
                            'robot_id' => $robotId,
                            'room_id' => $k,
                            'to_id' => $roomWxid,
                        ];

                        $targetModel = ChRoomBroadcastRecordTarget::create($recordTargetData);
                        $targetId = $targetModel->id;
                        $ext = [
                            'type' => 1,
                            'record_id' => $recordId,
                            'target_id' => $targetId
                        ];
                        // 判断是否是最后一个 chunk 的最后一个群
                        $isLastRoom = ($chunkIndex == $totalChunks - 1) && ($k == array_key_last($roomWxidMap));
                        if ($isLastRoom) {
                            // 在这里添加处理最后一个群的逻辑
                            $ext['is_last'] = true;
                        }else{
                            $ext['is_last'] = false;
                        }

                        $messageData['ext'] = $ext;
                        stdout_log()->info('群转播消息间隔'.$robotId.'_'.$delayTime);
                        container()->get(LowQueueService::class)->push(new CommonJob(new CommonJobDto(
                            classname: SendRobotMessageService::class,
                            method: 'handle',
                            data: $messageData
                        )),(int)$delayTime);
                    }
                }
            });
        }
    }

    public function records(array $params): array
    {
        $pageSize = $params['page_size'] ?? 10;

        $p = ChRoomBroadcastRecord::query()
            ->with(['robot' => function($query){
                $query->select(['id','nickname','avatar']);
            },'room' => function($query){
                $query->select(['id','room_name','room_avatar_url']);
            }])
            ->where('plan_id',$params['plan_id'])
            ->orderBy('created_at','desc')
            ->paginate((int)$pageSize);

        $p->getCollection()->transform(function ($item){
            if($item->completed_at){
                $item->times = smartDiffTimePrecise($item->created_at,$item->completed_at);
            }else{
                $item->times = '';
            }
            return $item;
        });

        return $this->mapper->setPaginate($p);
    }

    public function targets(array $params): array
    {
        $pageSize = $params['page_size'] ?? 10;

        $p = ChRoomBroadcastRecordTarget::query()
            ->with(['robot' => function($query){
                $query->select(['id','nickname','avatar']);
            },'room' => function($query){
                $query->select(['id','room_name','room_avatar_url']);
            }])
            ->where('record_id',$params['record_id'])
            ->orderBy('created_at','desc')
            ->paginate((int)$pageSize);

        return $this->mapper->setPaginate($p);
    }

    /**
     * 撤回消息
     * @param array $params
     * @return bool
     */
    public function recallMessage(array $params): bool
    {
        $info = ChRoomBroadcastRecordTarget::query()
            ->where('id',$params['id'])
            ->first();
        if($info && $info->status == 1){
            $robotInfo = $this->robotMapper->findById($info->robot_id);
            if(empty($robotInfo) || $robotInfo->login_status != 1){
                throw  new NormalStatusException('请先登录企微号');
            }

            $service = make(Robot::class, [$robotInfo->server_id, $robotInfo->uuid])->gateway();
            $rst = $service->revokeMsg($info->room_id, $info->msg_server_id);
            if($rst !== false){
                $info->status = 3;//已撤回
                $info->save();
                return true;
            }
        }
        return false;
    }

    public function resend(array $params)
    {
        $info = ChRoomBroadcastRecordTarget::query()
            ->with(['record' => function ($query) {
                $query->select(['id','content','msg_data']);
            }])
            ->where('id',$params['id'])
            ->first();

        if($info && !empty($info->record)){
            $data = $info->record->content;
            $msgType = $data['msg_type'];
            switch ($msgType){
                case MsgTypeConstants::TEXT:
                    $sendContent = $data['content'];
                    break;
                default:
                    $sendContent = $info->record->msg_data;
                    break;
            }
            $robotInfo = $this->robotMapper->findById($info->robot_id);
            if(empty($robotInfo) || $robotInfo->login_status != 1){
                throw  new NormalStatusException('请先登录企微号');
            }
            $msgData = new RobotMsgDto();
            $msgData->setMsgType($msgType)
                ->setContent($sendContent)
                ->setToId($info->to_id)
                ->setServerId($robotInfo->server_id)
                ->setUuid($robotInfo->uuid);

            //消息发送服务
            $result = make(\App\Robot\Service\RobotMessageService::class, [$msgData])->sendMessage();
            if($result !== false){
                $info->status = 1;
                $info->save();
                return true;
            }
        }
        return false;
    }
}